#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "ILexer.h"
#include "Scintilla.h"
#include "StringCopy.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "CellBuffer.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "XPM.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "CharClassify.h"
#include "Decoration.h"
#include "CaseFolder.h"
#include "Document.h"
#include "UniConversion.h"
#include "Selection.h"
#include "PositionCache.h"
#include "EditModel.h"
#include "MarginView.h"
#include "EditView.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

#ifdef SCI_NAMESPACE
namespace Scintilla {
#endif

  void DrawWrapMarker( Surface *surface, PRectangle rcPlace,
                       bool isEndMarker, ColourDesired wrapColour ) {
    surface->PenColour( wrapColour );
    enum { xa = 1 };
    int w = static_cast<int>( rcPlace.right - rcPlace.left ) - xa - 1;
    bool xStraight = isEndMarker;
    int x0 = static_cast<int>( xStraight ? rcPlace.left : rcPlace.right - 1 );
    int y0 = static_cast<int>( rcPlace.top );
    int dy = static_cast<int>( rcPlace.bottom - rcPlace.top ) / 5;
    int y = static_cast<int>( rcPlace.bottom - rcPlace.top ) / 2 + dy;
    struct Relative {
      Surface *surface;
      int xBase;
      int xDir;
      int yBase;
      int yDir;
      void MoveTo( int xRelative, int yRelative ) {
        surface->MoveTo( xBase + xDir * xRelative, yBase + yDir * yRelative );
      }
      void LineTo( int xRelative, int yRelative ) {
        surface->LineTo( xBase + xDir * xRelative, yBase + yDir * yRelative );
      }
    };
    Relative rel = { surface, x0, xStraight ? 1 : -1, y0, 1 };
    rel.MoveTo( xa, y );
    rel.LineTo( xa + 2 * w / 3, y - dy );
    rel.MoveTo( xa, y );
    rel.LineTo( xa + 2 * w / 3, y + dy );
    rel.MoveTo( xa, y );
    rel.LineTo( xa + w, y );
    rel.LineTo( xa + w, y - 2 * dy );
    rel.LineTo( xa - 1,
                y - 2 * dy );
  }

  MarginView::MarginView() {
    pixmapSelMargin = 0;
    pixmapSelPattern = 0;
    pixmapSelPatternOffset1 = 0;
    wrapMarkerPaddingRight = 3;
    customDrawWrapMarker = NULL;
  }

  void MarginView::DropGraphics( bool freeObjects ) {
    if( freeObjects ) {
      delete pixmapSelMargin;
      pixmapSelMargin = 0;
      delete pixmapSelPattern;
      pixmapSelPattern = 0;
      delete pixmapSelPatternOffset1;
      pixmapSelPatternOffset1 = 0;
    } else {
      if( pixmapSelMargin ) {
        pixmapSelMargin->Release();
      }
      if( pixmapSelPattern ) {
        pixmapSelPattern->Release();
      }
      if( pixmapSelPatternOffset1 ) {
        pixmapSelPatternOffset1->Release();
      }
    }
  }

  void MarginView::AllocateGraphics( const ViewStyle &vsDraw ) {
    if( !pixmapSelMargin ) {
      pixmapSelMargin = Surface::Allocate( vsDraw.technology );
    }
    if( !pixmapSelPattern ) {
      pixmapSelPattern = Surface::Allocate( vsDraw.technology );
    }
    if( !pixmapSelPatternOffset1 ) {
      pixmapSelPatternOffset1 = Surface::Allocate( vsDraw.technology );
    }
  }

  void MarginView::RefreshPixMaps( Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw ) {
    if( !pixmapSelPattern->Initialised() ) {
      const int patternSize = 8;
      pixmapSelPattern->InitPixMap( patternSize, patternSize, surfaceWindow, wid );
      pixmapSelPatternOffset1->InitPixMap( patternSize, patternSize, surfaceWindow, wid );
      PRectangle rcPattern = PRectangle::FromInts( 0, 0, patternSize, patternSize );
      ColourDesired colourFMFill = vsDraw.selbar;
      ColourDesired colourFMStripes = vsDraw.selbarlight;
      if( !( vsDraw.selbarlight == ColourDesired( 0xff, 0xff, 0xff ) ) ) {
        colourFMFill = vsDraw.selbarlight;
      }
      if( vsDraw.foldmarginColour.isSet ) {
        colourFMFill = vsDraw.foldmarginColour;
      }
      if( vsDraw.foldmarginHighlightColour.isSet ) {
        colourFMStripes = vsDraw.foldmarginHighlightColour;
      }
      pixmapSelPattern->FillRectangle( rcPattern, colourFMFill );
      pixmapSelPatternOffset1->FillRectangle( rcPattern, colourFMStripes );
      for( int y = 0; y < patternSize; y++ ) {
        for( int x = y % 2; x < patternSize; x += 2 ) {
          PRectangle rcPixel = PRectangle::FromInts( x, y, x + 1, y + 1 );
          pixmapSelPattern->FillRectangle( rcPixel, colourFMStripes );
          pixmapSelPatternOffset1->FillRectangle( rcPixel, colourFMFill );
        }
      }
    }
  }

  static int SubstituteMarkerIfEmpty( int markerCheck, int markerDefault, const ViewStyle &vs ) {
    if( vs.markers[markerCheck].markType == SC_MARK_EMPTY ) {
      return markerDefault;
    }
    return markerCheck;
  }

  void MarginView::PaintMargin( Surface *surface, int topLine, PRectangle rc, PRectangle rcMargin,
                                const EditModel &model, const ViewStyle &vs ) {
    PRectangle rcSelMargin = rcMargin;
    rcSelMargin.right = rcMargin.left;
    if( rcSelMargin.bottom < rc.bottom ) {
      rcSelMargin.bottom = rc.bottom;
    }
    Point ptOrigin = model.GetVisibleOriginInMain();
    FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font;
    for( int margin = 0; margin <= SC_MAX_MARGIN; margin++ ) {
      if( vs.ms[margin].width > 0 ) {
        rcSelMargin.left = rcSelMargin.right;
        rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
        if( vs.ms[margin].style != SC_MARGIN_NUMBER ) {
          if( vs.ms[margin].mask & SC_MASK_FOLDERS ) {
            bool invertPhase = static_cast<int>( ptOrigin.y ) & 1;
            surface->FillRectangle( rcSelMargin,
                                    invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1 );
          } else {
            ColourDesired colour;
            switch( vs.ms[margin].style ) {
              case SC_MARGIN_BACK:
                colour = vs.styles[STYLE_DEFAULT].back;
                break;
              case SC_MARGIN_FORE:
                colour = vs.styles[STYLE_DEFAULT].fore;
                break;
              default:
                colour = vs.styles[STYLE_LINENUMBER].back;
                break;
            }
            surface->FillRectangle( rcSelMargin, colour );
          }
        } else
        { surface->FillRectangle( rcSelMargin, vs.styles[STYLE_LINENUMBER].back ); }
        const int lineStartPaint = static_cast<int>( rcMargin.top + ptOrigin.y ) / vs.lineHeight;
        int visibleLine = model.TopLineOfMain() + lineStartPaint;
        int yposScreen = lineStartPaint * vs.lineHeight - static_cast<int>( ptOrigin.y );
        bool needWhiteClosure = false;
        if( vs.ms[margin].mask & SC_MASK_FOLDERS ) {
          int level = model.pdoc->GetLevel( model.cs.DocFromDisplay( visibleLine ) );
          if( level & SC_FOLDLEVELWHITEFLAG ) {
            int lineBack = model.cs.DocFromDisplay( visibleLine );
            int levelPrev = level;
            while( ( lineBack > 0 ) && ( levelPrev & SC_FOLDLEVELWHITEFLAG ) ) {
              lineBack--;
              levelPrev = model.pdoc->GetLevel( lineBack );
            }
            if( !( levelPrev & SC_FOLDLEVELHEADERFLAG ) ) {
              if( ( level & SC_FOLDLEVELNUMBERMASK ) < ( levelPrev & SC_FOLDLEVELNUMBERMASK ) )
              { needWhiteClosure = true; }
            }
          }
          if( highlightDelimiter.isEnabled ) {
            int lastLine = model.cs.DocFromDisplay( topLine + model.LinesOnScreen() ) + 1;
            model.pdoc->GetHighlightDelimiters( highlightDelimiter, model.pdoc->LineFromPosition( model.sel.MainCaret() ), lastLine );
          }
        }
        const int folderOpenMid = SubstituteMarkerIfEmpty( SC_MARKNUM_FOLDEROPENMID,
                                  SC_MARKNUM_FOLDEROPEN, vs );
        const int folderEnd = SubstituteMarkerIfEmpty( SC_MARKNUM_FOLDEREND,
                              SC_MARKNUM_FOLDER, vs );
        while( ( visibleLine < model.cs.LinesDisplayed() ) && yposScreen < rc.bottom ) {
          PLATFORM_ASSERT( visibleLine < model.cs.LinesDisplayed() );
          const int lineDoc = model.cs.DocFromDisplay( visibleLine );
          PLATFORM_ASSERT( model.cs.GetVisible( lineDoc ) );
          const int firstVisibleLine = model.cs.DisplayFromDoc( lineDoc );
          const int lastVisibleLine = model.cs.DisplayLastFromDoc( lineDoc );
          const bool firstSubLine = visibleLine == firstVisibleLine;
          const bool lastSubLine = visibleLine == lastVisibleLine;
          int marks = model.pdoc->GetMark( lineDoc );
          if( !firstSubLine ) {
            marks = 0;
          }
          bool headWithTail = false;
          if( vs.ms[margin].mask & SC_MASK_FOLDERS ) {
            const int level = model.pdoc->GetLevel( lineDoc );
            const int levelNext = model.pdoc->GetLevel( lineDoc + 1 );
            const int levelNum = level & SC_FOLDLEVELNUMBERMASK;
            const int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
            if( level & SC_FOLDLEVELHEADERFLAG ) {
              if( firstSubLine ) {
                if( levelNum < levelNextNum ) {
                  if( model.cs.GetExpanded( lineDoc ) ) {
                    if( levelNum == SC_FOLDLEVELBASE )
                    { marks |= 1 << SC_MARKNUM_FOLDEROPEN; }
                    else
                    { marks |= 1 << folderOpenMid; }
                  } else {
                    if( levelNum == SC_FOLDLEVELBASE )
                    { marks |= 1 << SC_MARKNUM_FOLDER; }
                    else
                    { marks |= 1 << folderEnd; }
                  }
                } else if( levelNum > SC_FOLDLEVELBASE )
                { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
              } else {
                if( levelNum < levelNextNum ) {
                  if( model.cs.GetExpanded( lineDoc ) )
                  { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
                  else if( levelNum > SC_FOLDLEVELBASE )
                  { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
                } else if( levelNum > SC_FOLDLEVELBASE )
                { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
              }
              needWhiteClosure = false;
              const int firstFollowupLine = model.cs.DocFromDisplay( model.cs.DisplayFromDoc( lineDoc + 1 ) );
              const int firstFollowupLineLevel = model.pdoc->GetLevel( firstFollowupLine );
              const int secondFollowupLineLevelNum = model.pdoc->GetLevel( firstFollowupLine + 1 ) & SC_FOLDLEVELNUMBERMASK;
              if( !model.cs.GetExpanded( lineDoc ) ) {
                if( ( firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG ) &&
                    ( levelNum > secondFollowupLineLevelNum ) )
                { needWhiteClosure = true; }
                if( highlightDelimiter.IsFoldBlockHighlighted( firstFollowupLine ) )
                { headWithTail = true; }
              }
            } else if( level & SC_FOLDLEVELWHITEFLAG ) {
              if( needWhiteClosure ) {
                if( levelNext & SC_FOLDLEVELWHITEFLAG )
                { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
                else if( levelNextNum > SC_FOLDLEVELBASE ) {
                  marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
                  needWhiteClosure = false;
                } else {
                  marks |= 1 << SC_MARKNUM_FOLDERTAIL;
                  needWhiteClosure = false;
                }
              } else if( levelNum > SC_FOLDLEVELBASE ) {
                if( levelNextNum < levelNum ) {
                  if( levelNextNum > SC_FOLDLEVELBASE )
                  { marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; }
                  else
                  { marks |= 1 << SC_MARKNUM_FOLDERTAIL; }
                } else
                { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
              }
            } else if( levelNum > SC_FOLDLEVELBASE ) {
              if( levelNextNum < levelNum ) {
                needWhiteClosure = false;
                if( levelNext & SC_FOLDLEVELWHITEFLAG ) {
                  marks |= 1 << SC_MARKNUM_FOLDERSUB;
                  needWhiteClosure = true;
                } else if( lastSubLine ) {
                  if( levelNextNum > SC_FOLDLEVELBASE )
                  { marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL; }
                  else
                  { marks |= 1 << SC_MARKNUM_FOLDERTAIL; }
                } else
                { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
              } else
              { marks |= 1 << SC_MARKNUM_FOLDERSUB; }
            }
          }
          int changed = model.pdoc->GetChanged( lineDoc );
          if( changed == 1 ) {
            marks |= 1 << SC_MARKNUM_CHANGEUNSAVED;
          }
          if( changed == 2 ) {
            marks |= 1 << SC_MARKNUM_CHANGESAVED;
          }
          marks &= vs.ms[margin].mask;
          PRectangle rcMarker = rcSelMargin;
          rcMarker.top = static_cast<XYPOSITION>( yposScreen );
          rcMarker.bottom = static_cast<XYPOSITION>( yposScreen + vs.lineHeight );
          if( vs.ms[margin].style == SC_MARGIN_NUMBER ) {
            if( firstSubLine ) {
              char number[100] = "";
              if( lineDoc >= 0 )
              { sprintf( number, "%d", lineDoc + 1 ); }
              if( model.foldFlags & ( SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE ) ) {
                if( model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS ) {
                  int lev = model.pdoc->GetLevel( lineDoc );
                  sprintf( number, "%c%c %03X %03X",
                           ( lev & SC_FOLDLEVELHEADERFLAG ) ? 'H' : '_',
                           ( lev & SC_FOLDLEVELWHITEFLAG ) ? 'W' : '_',
                           lev & SC_FOLDLEVELNUMBERMASK,
                           lev >> 16
                         );
                } else {
                  int state = model.pdoc->GetLineState( lineDoc );
                  sprintf( number, "%0X", state );
                }
              }
              PRectangle rcNumber = rcMarker;
              XYPOSITION width = surface->WidthText( fontLineNumber, number, static_cast<int>( strlen( number ) ) );
              XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
              rcNumber.left = xpos;
              DrawTextNoClipPhase( surface, rcNumber, vs.styles[STYLE_LINENUMBER],
                                   rcNumber.top + vs.maxAscent, number, static_cast<int>( strlen( number ) ), drawAll );
            } else if( vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN ) {
              PRectangle rcWrapMarker = rcMarker;
              rcWrapMarker.right -= wrapMarkerPaddingRight;
              rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
              if( customDrawWrapMarker == NULL )
              { DrawWrapMarker( surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore ); }
              else
              { customDrawWrapMarker( surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore ); }
            }
          } else if( vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT ) {
            const StyledText stMargin = model.pdoc->MarginStyledText( lineDoc );
            if( stMargin.text && ValidStyledText( vs, vs.marginStyleOffset, stMargin ) ) {
              if( firstSubLine ) {
                surface->FillRectangle( rcMarker,
                                        vs.styles[stMargin.StyleAt( 0 ) + vs.marginStyleOffset].back );
                if( vs.ms[margin].style == SC_MARGIN_RTEXT ) {
                  int width = WidestLineWidth( surface, vs, vs.marginStyleOffset, stMargin );
                  rcMarker.left = rcMarker.right - width - 3;
                }
                DrawStyledText( surface, vs, vs.marginStyleOffset, rcMarker,
                                stMargin, 0, stMargin.length, drawAll );
              } else {
                const int annotationLines = model.pdoc->AnnotationLines( lineDoc );
                if( annotationLines && ( visibleLine > lastVisibleLine - annotationLines ) )
                { surface->FillRectangle( rcMarker, vs.styles[stMargin.StyleAt( 0 ) + vs.marginStyleOffset].back ); }
              }
            }
          }
          if( marks ) {
            for( int markBit = 0; ( markBit < 32 ) && marks; markBit++ ) {
              if( marks & 1 ) {
                LineMarker::typeOfFold tFold = LineMarker::undefined;
                if( ( vs.ms[margin].mask & SC_MASK_FOLDERS ) && highlightDelimiter.IsFoldBlockHighlighted( lineDoc ) ) {
                  if( highlightDelimiter.IsBodyOfFoldBlock( lineDoc ) )
                  { tFold = LineMarker::body; }
                  else if( highlightDelimiter.IsHeadOfFoldBlock( lineDoc ) ) {
                    if( firstSubLine )
                    { tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head; }
                    else {
                      if( model.cs.GetExpanded( lineDoc ) || headWithTail )
                      { tFold = LineMarker::body; }
                      else
                      { tFold = LineMarker::undefined; }
                    }
                  } else if( highlightDelimiter.IsTailOfFoldBlock( lineDoc ) )
                  { tFold = LineMarker::tail; }
                }
                vs.markers[markBit].Draw( surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style );
              }
              marks >>= 1;
            }
          }
          visibleLine++;
          yposScreen += vs.lineHeight;
        }
      }
    }
    PRectangle rcBlankMargin = rcMargin;
    rcBlankMargin.left = rcSelMargin.right;
    surface->FillRectangle( rcBlankMargin, vs.styles[STYLE_DEFAULT].back );
  }

  #ifdef SCI_NAMESPACE
}
  #endif

